package org.proteored.miapeapi.xml.pride.msi;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.proteored.miapeapi.cv.Accession;
import org.proteored.miapeapi.cv.ControlVocabularyManager;
import org.proteored.miapeapi.cv.msi.PrideProject;
import org.proteored.miapeapi.exceptions.IllegalMiapeArgumentException;
import org.proteored.miapeapi.exceptions.MiapeDatabaseException;
import org.proteored.miapeapi.exceptions.MiapeSecurityException;
import org.proteored.miapeapi.interfaces.Software;
import org.proteored.miapeapi.interfaces.User;
import org.proteored.miapeapi.interfaces.msi.IdentifiedPeptide;
import org.proteored.miapeapi.interfaces.msi.IdentifiedProtein;
import org.proteored.miapeapi.interfaces.msi.IdentifiedProteinSet;
import org.proteored.miapeapi.interfaces.msi.InputDataSet;
import org.proteored.miapeapi.interfaces.msi.InputParameter;
import org.proteored.miapeapi.interfaces.msi.MSIAdditionalInformation;
import org.proteored.miapeapi.interfaces.msi.MiapeMSIDocument;
import org.proteored.miapeapi.interfaces.msi.Validation;
import org.proteored.miapeapi.interfaces.persistence.PersistenceManager;
import org.proteored.miapeapi.interfaces.xml.MiapeXmlFile;
import org.proteored.miapeapi.validation.ValidationReport;
import org.proteored.miapeapi.validation.msi.MiapeMSIValidator;
import org.proteored.miapeapi.xml.msi.MiapeMSIXmlFactory;
import org.proteored.miapeapi.xml.pride.AbstractDocumentFromPride;
import org.proteored.miapeapi.xml.pride.autogenerated.CvParamType;
import org.proteored.miapeapi.xml.pride.autogenerated.DescriptionType;
import org.proteored.miapeapi.xml.pride.autogenerated.ExperimentType;
import org.proteored.miapeapi.xml.pride.autogenerated.GelFreeIdentificationType;
import org.proteored.miapeapi.xml.pride.autogenerated.ParamType;
import org.proteored.miapeapi.xml.pride.autogenerated.TwoDimensionalIdentificationType;
import org.proteored.miapeapi.xml.pride.autogenerated.UserParamType;
import org.proteored.miapeapi.xml.pride.util.Parameter;
import org.proteored.miapeapi.xml.pride.util.Utils;
import org.proteored.miapeapi.xml.util.MiapeXmlUtil;

public class MiapeMSIDocumentImpl extends AbstractDocumentFromPride implements
		MiapeMSIDocument {
	// private final XmlMiapeFactory<MiapeMSIDocument> xmlFactory;
	private int msDocumentID;
	private final Set<InputDataSet> inputDataSets = new HashSet<InputDataSet>();
	private final Set<IdentifiedProteinSet> proteinSets = new HashSet<IdentifiedProteinSet>();
	private final Set<InputParameter> inputParameters = new HashSet<InputParameter>();
	private Set<Software> MSISoftwares;

	public MiapeMSIDocumentImpl(ExperimentType experiment, User user,
			String prideXMLFileName, String projectName,
			ControlVocabularyManager cvManager)
			throws IllegalMiapeArgumentException {
		super(experiment, cvManager, user, prideXMLFileName, projectName);
		processPRIDEFile(experiment);
	}

	public MiapeMSIDocumentImpl(ExperimentType experiment,
			PersistenceManager dbManager, ControlVocabularyManager cvManager,
			String user, String password, String prideXMLFileName,
			String projectName) throws MiapeDatabaseException,
			MiapeSecurityException, IllegalMiapeArgumentException {
		super(experiment, dbManager, cvManager, user, password,
				prideXMLFileName, projectName);
		processPRIDEFile(experiment);

	}

	private void processPRIDEFile(ExperimentType experiment) {
		// InputDataSet
		// One inputDataSet
		inputDataSets.add(new InputDataSetImpl(experiment.getMzData(),
				MiapeXmlUtil.InputDataSetCounter.increaseCounter()));

		// MSI sofwares
		MSISoftwares = getMSISoftwares(this.experiment);

		// InputParameter
		final InputParameterImpl inputParameter = new InputParameterImpl(
				experiment, MSISoftwares,
				MiapeXmlUtil.ParameterCounter.increaseCounter(), cvManager);
		inputParameters.add(inputParameter);

		// One protein set for all the proteins
		proteinSets.add(new ProteinSetImpl(this.experiment, inputDataSets,
				inputParameter, cvManager));

	}

	private Set<Software> getMSISoftwares(ExperimentType experiment) {
		Set<Software> result = new HashSet<Software>();
		Set<String> softwares = new HashSet<String>();
		for (Object object : experiment.getGelFreeIdentification()) {
			GelFreeIdentificationType identification = (GelFreeIdentificationType) object;
			if (identification.getSearchEngine() != null
					&& softwares.contains(identification.getSearchEngine()) == false) {
				softwares.add(identification.getSearchEngine());
			}
		}
		for (Object object : experiment.getTwoDimensionalIdentification()) {
			TwoDimensionalIdentificationType identification = (TwoDimensionalIdentificationType) object;
			if (identification.getSearchEngine() != null
					&& softwares.contains(identification.getSearchEngine()) == false) {
				softwares.add(identification.getSearchEngine());
			}
		}
		for (String software : softwares) {
			result.add(new SoftwareImpl(software, MiapeXmlUtil.SoftwareCounter
					.increaseCounter()));
		}
		return result;
	}

	@Override
	public String getGeneratedFilesDescription() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public String getGeneratedFilesURI() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Set<InputDataSet> getInputDataSets() {

		if (!inputDataSets.isEmpty())
			return inputDataSets;
		return null;
	}

	@Override
	public Set<InputParameter> getInputParameters() {
		if (!inputParameters.isEmpty())
			return inputParameters;
		return null;
	}

	@Override
	public Set<Software> getSoftwares() {
		return MSISoftwares;
	}

	@Override
	public Set<Validation> getValidations() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public int store() throws MiapeDatabaseException, MiapeSecurityException {
		if (dbManager != null
				&& dbManager.getMiapeMSIPersistenceManager() != null) {
			return dbManager.getMiapeMSIPersistenceManager().store(this);
		}
		throw new MiapeDatabaseException(
				"The persistance method is not defined.");
	}

	@Override
	public void delete(String user, String password)
			throws MiapeDatabaseException, MiapeSecurityException {
		if (dbManager != null
				&& dbManager.getMiapeMSIPersistenceManager() != null) {
			dbManager.getMiapeMSIPersistenceManager().deleteById(getId(), user,
					password);
		}
		throw new MiapeDatabaseException(
				"The persistance method is not defined.");
	}

	@Override
	public MiapeXmlFile<MiapeMSIDocument> toXml() {
		return MiapeMSIXmlFactory.getFactory().toXml(this, cvManager);
	}

	@Override
	public Set<MSIAdditionalInformation> getAdditionalInformations() {
		Set<MSIAdditionalInformation> setOfAdditionalInformations = new HashSet<MSIAdditionalInformation>();

		if (experiment.getProtocol() != null) {

			if (experiment.getProtocol().getProtocolName() != null) {
				setOfAdditionalInformations.add(new AdditionalInformationImpl(
						"Protocol name", experiment.getProtocol()
								.getProtocolName()));
			}

			if (experiment.getProtocol().getProtocolSteps() != null) {
				for (ParamType paramType : experiment.getProtocol()
						.getProtocolSteps().getStepDescription()) {
					List<Parameter> listParameter = Utils.createListParameter(
							paramType.getCvParamOrUserParam(), null);
					if (listParameter != null)
						for (Parameter param : listParameter) {
							setOfAdditionalInformations
									.add(new AdditionalInformationImpl(param
											.getName(), param.getValue()));
						}
				}
			}
			if (experiment.getTitle() != null)
				setOfAdditionalInformations.add(new AdditionalInformationImpl(
						"PRIDE title", experiment.getTitle()));
			if (experiment.getShortLabel() != null)
				setOfAdditionalInformations.add(new AdditionalInformationImpl(
						"PRIDE short label", experiment.getShortLabel()));

			if (experiment.getMzData() != null
					&& experiment.getMzData().getDescription() != null
					&& experiment.getMzData().getDescription().getAdmin() != null
					&& experiment.getMzData().getDescription().getAdmin() != null) {
				// sample name
				if (experiment.getMzData().getDescription().getAdmin()
						.getSampleName() != null) {
					setOfAdditionalInformations
							.add(new AdditionalInformationImpl("sample name",
									experiment.getMzData().getDescription()
											.getAdmin().getSampleName()));
				}
				// sample description
				final DescriptionType sampleDescription = experiment
						.getMzData().getDescription().getAdmin()
						.getSampleDescription();
				if (sampleDescription != null
						&& sampleDescription.getCvParamOrUserParam() != null) {
					for (Object cvOrUserParam : sampleDescription
							.getCvParamOrUserParam()) {
						if (cvOrUserParam instanceof CvParamType)
							setOfAdditionalInformations
									.add(new AdditionalInformationImpl(
											(CvParamType) cvOrUserParam));
						if (cvOrUserParam instanceof UserParamType)
							setOfAdditionalInformations
									.add(new AdditionalInformationImpl(
											(UserParamType) cvOrUserParam));
					}

				}
			}
		}
		if (!setOfAdditionalInformations.isEmpty())
			return setOfAdditionalInformations;

		return null;
	}

	@Override
	public ValidationReport getValidationReport() {
		return MiapeMSIValidator.getInstance().getReport(this);
	}

	@Override
	public Set<IdentifiedProteinSet> getIdentifiedProteinSets() {
		if (!proteinSets.isEmpty())
			return proteinSets;
		return null;

	}

	@Override
	public String getName() {
		// Try to obtain the name of the CV=PRIDE:0000097 -> project
		if (experiment.getAdditional() != null) {
			for (Object cvOrUserParam : experiment.getAdditional()
					.getCvParamOrUserParam()) {
				if (cvOrUserParam instanceof CvParamType) {
					final Accession cvAcc = cvManager.getControlVocabularyId(
							((CvParamType) cvOrUserParam).getName(),
							PrideProject.getInstance(cvManager));
					if (cvAcc != null)
						return ((CvParamType) cvOrUserParam).getValue();
				} else if (cvOrUserParam instanceof UserParamType) {
					final Accession cvAcc = cvManager.getControlVocabularyId(
							((UserParamType) cvOrUserParam).getName(),
							PrideProject.getInstance(cvManager));
					if (cvAcc != null)
						return ((UserParamType) cvOrUserParam).getValue();
				}
			}
		}
		// if not, return a default name
		return "MIAPE MSI from PRIDE file";

	}

	@Override
	public void setReferencedMSDocument(int msDocument) {
		msDocumentID = msDocument;

	}

	@Override
	public int getMSDocumentReference() {
		return msDocumentID;
	}

	@Override
	public List<IdentifiedPeptide> getIdentifiedPeptides() {
		// In case of PRIDE file, all peptides are assigned to a protein, so to
		// retrieve the
		// peptides, we can iterate the proteins to get the peptides from each
		// protein
		List<IdentifiedPeptide> ret = new ArrayList<IdentifiedPeptide>();
		final Set<IdentifiedProteinSet> identifiedProteinSets = getIdentifiedProteinSets();
		if (identifiedProteinSets != null) {
			for (IdentifiedProteinSet proteinSet : identifiedProteinSets) {
				if (proteinSet.getIdentifiedProteins() != null) {
					for (String proteinAcc : proteinSet.getIdentifiedProteins()
							.keySet()) {
						IdentifiedProtein protein = proteinSet
								.getIdentifiedProteins().get(proteinAcc);
						ret.addAll(protein.getIdentifiedPeptides());
					}
				}
			}
		}
		return ret;
	}

	public void setDbManager(PersistenceManager dbmanager) {
		dbManager = dbmanager;

	}

}
